EDA¶

In [20]:
import pandas as pd
import numpy as np
import umap
#import seaborn as sns
import plotly.express as px
#pip install pyarrow
#pip install seaborn
#pip install umap-learn

from sklearn.preprocessing import StandardScaler
In [25]:
#Train

data1 = pd.read_parquet('train_numerical_features.parquet')
data2 = pd.read_parquet('train_text_features.parquet')
In [26]:
data = data1.merge(data2, on = ['id', 'tagline', 'credits', 'title'])
In [28]:
data.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 9641 entries, 0 to 9640
Data columns (total 18 columns):
 #   Column                Non-Null Count  Dtype  
---  ------                --------------  -----  
 0   id                    9641 non-null   int64  
 1   title                 9641 non-null   object 
 2   budget                9641 non-null   float64
 3   revenue               9641 non-null   float64
 4   runtime               9641 non-null   float64
 5   status                9641 non-null   object 
 6   tagline               7603 non-null   object 
 7   credits               9623 non-null   object 
 8   poster_path           9641 non-null   object 
 9   backdrop_path         9632 non-null   object 
 10  recommendations       9639 non-null   object 
 11  genres                9639 non-null   object 
 12  original_language     9641 non-null   object 
 13  overview              9628 non-null   object 
 14  production_companies  9565 non-null   object 
 15  release_date          9641 non-null   object 
 16  keywords              9052 non-null   object 
 17  vote_average          9641 non-null   float64
dtypes: float64(4), int64(1), object(13)
memory usage: 1.4+ MB
In [29]:
data.drop(['poster_path', 'backdrop_path', 'recommendations'], axis=1, inplace=True)

data = data[~ (
    (data['revenue']==0) |
    (data['release_date'].isna()) |
    (data['runtime'].isna()) |
    ((data['status'] != 'Released'))
                )]

data['release_date'] = pd.to_datetime(data['release_date'], format = '%Y-%m-%d')

data_cat = data.select_dtypes(include=[object])
data[data_cat.columns] = data_cat.fillna('')

data.loc[:, 'label'] = 'Very Positive'

data.loc[(data['vote_average'] <= 8) & (data['vote_average'] > 7), 'label'] = 'Positive'
data.loc[(data['vote_average'] <= 7) & (data['vote_average'] > 6), 'label'] = 'Mostly Positive'
data.loc[(data['vote_average'] <= 6) & (data['vote_average'] > 5), 'label'] = 'Mixed'
data.loc[(data['vote_average'] <= 5) , 'label'] = 'Negative'

data.drop(['vote_average', 'id', 'status'], axis=1, inplace=True)
data.rename(columns = {'revenue':'target'},
            inplace=True)
In [30]:
data.head(3)
Out[30]:
title budget target runtime tagline credits genres original_language overview production_companies release_date keywords label
0 Fantastic Beasts: The Secrets of Dumbledore 200000000.0 400000000.0 142.0 Return to the magic. Jude Law-Eddie Redmayne-Mads Mikkelsen-Ezra Mi... Fantasy-Adventure-Action en Professor Albus Dumbledore knows the powerful ... Warner Bros. Pictures-Heyday Films 2022-04-06 magic-curse-fantasy world-wizard-magical creat... Mostly Positive
1 Sonic the Hedgehog 2 110000000.0 393000000.0 122.0 Welcome to the next level. James Marsden-Ben Schwartz-Tika Sumpter-Natash... Action-Adventure-Family-Comedy en After settling in Green Hills Sonic is eager t... Original Film-Blur Studio-Marza Animation Plan... 2022-03-30 sequel-based on video game-hedgehog-live actio... Positive
2 The Lost City 74000000.0 164289828.0 112.0 The adventure is real. The heroes are not. Sandra Bullock-Channing Tatum-Daniel Radcliffe... Action-Adventure-Comedy en A reclusive romance novelist was sure nothing ... Paramount-Fortis Films-3dot Productions-Exhibi... 2022-03-24 duringcreditsstinger Mostly Positive
In [32]:
data.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 6451 entries, 0 to 9636
Data columns (total 13 columns):
 #   Column                Non-Null Count  Dtype         
---  ------                --------------  -----         
 0   title                 6451 non-null   object        
 1   budget                6451 non-null   float64       
 2   target                6451 non-null   float64       
 3   runtime               6451 non-null   float64       
 4   tagline               6451 non-null   object        
 5   credits               6451 non-null   object        
 6   genres                6451 non-null   object        
 7   original_language     6451 non-null   object        
 8   overview              6451 non-null   object        
 9   production_companies  6451 non-null   object        
 10  release_date          6451 non-null   datetime64[ns]
 11  keywords              6451 non-null   object        
 12  label                 6451 non-null   object        
dtypes: datetime64[ns](1), float64(3), object(9)
memory usage: 705.6+ KB

EDA¶

Tipos de datos¶

In [6]:
data.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 6451 entries, 0 to 9636
Data columns (total 13 columns):
 #   Column                Non-Null Count  Dtype         
---  ------                --------------  -----         
 0   title                 6451 non-null   object        
 1   budget                6451 non-null   float64       
 2   target                6451 non-null   float64       
 3   runtime               6451 non-null   float64       
 4   tagline               6451 non-null   object        
 5   credits               6451 non-null   object        
 6   genres                6451 non-null   object        
 7   original_language     6451 non-null   object        
 8   overview              6451 non-null   object        
 9   production_companies  6451 non-null   object        
 10  release_date          6451 non-null   datetime64[ns]
 11  keywords              6451 non-null   object        
 12  label                 6451 non-null   object        
dtypes: datetime64[ns](1), float64(3), object(9)
memory usage: 705.6+ KB

Tenemos 3 columnas numéricas, 1 de temporal y 9 que representan texto. Dado el preprocesamiento inicial, ya no hay datos faltantes.

Numéricas¶

In [7]:
data_num = data.select_dtypes(include=[np.number])
In [8]:
for col in data_num.columns:
    fig = px.histogram(data, x=col,
                       title=f'Histograma de la variable "{col}"')
    fig.show()
  • La variable Budget está muy concentrada en el intervalo [0, 5M], posteriormente empieza a decrecer de forma significativa. Dada la distribución podríamos estar en presencia de outliers.
  • La variable Target está concentrada en el rango [0, 20M] y luego empieza a decrecer. También hay outliers.
  • La variable runtime asemeja a una distribución normal con media cercana a los 100 minutos, es decir, 1h y 40minutos de película.
In [9]:
fig = px.imshow(data_num.corr(), text_auto=True)
fig.show()

La correlación de variables numéricas muestra que las películas con mayor presupuesto (budget) tienden a tener más ingresos. Esto puede deberse a que grandes cadenas destinen mucho dinero en hacer películas muy atractivas para la audiencia (por ej. Marvel). No hay una relación clara entre duración y variables monetarias.

In [10]:
reducer = umap.UMAP()
In [11]:
scaled_num_data = StandardScaler().fit_transform(data_num.values)
In [12]:
embedding = reducer.fit_transform(scaled_num_data)
#embedding = reducer.fit_transform(data_num.values)
In [13]:
fig = px.scatter(x= embedding[:, 0], y = embedding[:, 1], color = data['label'])
fig.show()

La reducción de dimensionalidad muestra una leve diferencia entre las reseñas muy positivas y las negativas, las cuales se encuentran en lados opuestos de la gráfica. Los otros tipos de calificaciones se encuentran entre ambos extremos y abarcan la region media.

In [14]:
fig = px.scatter(y = data_num['budget'], x = data_num['target'], color = data['label'])
fig.show()

Del gráfico anterior una pelicula con mayor presupuesto no garantiza una mayor ganancia, pero si hay una correlación positiva entre ambas. Además, la mayoría de las peliculas con poca recaudación son mal evaluadas; por el contrario, mientras mayor recaudación, mejor evaluadas son.

In [15]:
fig = px.scatter(y = data_num['runtime'], x = data_num['target'], color = data['label'])
fig.show()

Para la duración de la pelicula, no pareciera haber una correlación entre el tiempo, la clasificación y las ganancias obtenidas.

In [16]:
fig = px.scatter(x = data_num['runtime'], y = data_num['budget'], color = data['label'])
fig.show()

La gráfica anterior muestra que películas con menor duració usualmente tienen un menor presupuesto, pero que esta no es la regla dado que hay peliculas de alta duración y presupuesto bajo.

Variables categóricas¶

In [17]:
data_cat = data[['genres', 'production_companies', 'credits', 'keywords', 'original_language']]
In [18]:
for col in data_cat.columns:
    if col != 'original_language':
        values = [x.split(sep='-') for x in data_cat[col].unique()]
        values = [i  for x in values for i in x]
    else:
        values = data_cat[col]
    val_unique, counts = np.unique(values, return_counts=True)
    series_count = pd.Series(counts, index = val_unique).sort_values(ascending = False).head(30)
    
    fig = px.bar(y = series_count.index, x = series_count.values)
    fig.show()

El gráfico anterior muestra la frecuencia de cada género en la base de datos. Los valores Drama, Action, Comedy y Adventure son los 4 más frecuentes, pero no hay una predominancia clara.